package com.chaber.containers.aspects;

import java.util.Calendar;
import java.util.Set;

import javax.validation.ConstraintViolation;
import javax.validation.ConstraintViolationException;

import org.apache.commons.lang.exception.ExceptionUtils;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;

import com.chaber.containers.beans.AbstractObjectData;
import com.chaber.containers.common.ConstraintViolationEvent;
import com.chaber.containers.services.AbstractService;
import com.mysql.jdbc.exceptions.jdbc4.MySQLIntegrityConstraintViolationException;

@Aspect
public class EntityPersistenceAspect {

	@Pointcut("execution(* com.chaber.containers.services.AbstractService+.*save*(..))")
	public void pointcutSaveEntity() {
	}

	@Before("pointcutSaveEntity() && args(entity)")
	public void beforeSaveEntity(AbstractObjectData entity) {
		if (entity != null && entity.getCreationDate() != null) {
			entity.setModificationDate(Calendar.getInstance().getTime());
		} else if (entity != null && entity.getCreationDate() == null) {
			entity.setCreationDate(Calendar.getInstance().getTime());
		}
	}

	@Around("pointcutSaveEntity() && args(entity)")
	public <TYPE extends AbstractObjectData> void aroundSaveEntity(ProceedingJoinPoint joinPoint, TYPE entity) throws Throwable {
		AbstractService service = (AbstractService) joinPoint.getTarget();

		Set<ConstraintViolation<TYPE>> constraintViolations = service.getValidator().validate(entity);
		if (!constraintViolations.isEmpty()) {
			ConstraintViolationEvent<TYPE> cViolationEvent = new ConstraintViolationEvent<>(service, constraintViolations);
			service.getEventPublisher().publishEvent(cViolationEvent);

			throw new ConstraintViolationException(constraintViolations);
		} else {
			try {
				joinPoint.proceed();
			} catch (Exception ex) {
				int throwableIndex = ExceptionUtils.indexOfThrowable(ex, MySQLIntegrityConstraintViolationException.class);
				if (throwableIndex != -1) {
					MySQLIntegrityConstraintViolationException constrEx = (MySQLIntegrityConstraintViolationException) ExceptionUtils.getThrowables(ex)[throwableIndex];
					
					ConstraintViolationEvent<TYPE> constrViolationEvent = new ConstraintViolationEvent<>(service, constrEx.getMessage());

					service.getEventPublisher().publishEvent(constrViolationEvent);
				}
				
				throw ex;
			}
		}
	}
}
